﻿using System;
using FM_Mono;
using UnityEngine;
using UObject = UnityEngine.Object;
using UnityEngine.Profiling;

namespace FibMatrix
{
    /// <summary>
    /// 资源的异步加载请求，各资源模式共用
    /// 每次请求都是唯一的request对象
    /// </summary>
    [IgnoreGen]
    internal class AsyncResourceLoadRequest : IAsyncResourceRequest, IResourcePipelineCallback
    {
        [Flags]
        enum Status : byte
        {
            None = 0,
            Instantiated = 0x1, // 需要实例化
            AssetAccessed = 0x2, // 获取了资源
            Done = 0x4, // 加载完成
            Disposed = 0x8, // request被销毁
        }

        public AsyncResourceLoadRequest(string id, IResourceFacadeImplement resFacade,
            IResourcePipelineContext context, AbstractLoadItem item, bool instantiated, int priority = 0)
        {
            this._id = id;
            this._resFacade = resFacade;
            this._context = context;
            this._item = item;
            this._status = instantiated ? Status.Instantiated : Status.Done; // 为什么done做初值？
            this._priority = priority;
        }

        private IResourceFacadeImplement _resFacade;

        private IResourcePipelineContext _context;

        private AbstractLoadItem _item;

        private Status _status;

        private readonly string _id;

        public string id
        {
            get { return _id; }
        }

        public bool instantiated
        {
            get { return (this._status & Status.Instantiated) != 0; }
        }

        public bool destroyed
        {
            get { return (this._status & Status.Disposed) != 0; }
        }

        private readonly int _priority;

        public int priority => _priority;

        public Action<IAsyncResourceRequest> callback { get; set; }

        public bool isDone
        {
            get { return (_status & Status.Done) != 0; }
        }

        private UObject _asset;
        protected internal UObject originalAsset => _asset;

        public UObject asset
        {
            get
            {
                if (destroyed)
                    Debug.LogError($"request disposed, {_id}");
                if (!isDone)
                {
                    Debug.LogError($"request not done, {_id}");
                    return null;
                }

                if ((_status & Status.AssetAccessed) == 0 && _asset != null)
                {
                    if (instantiated)
                    {
                        Profiler.BeginSample("AsyncResourceLoadRequest.asset");
                        Profiler.BeginSample(_item.itemID);
                        var instantiatedAsset = _resFacade.Instantiate(_asset, _item, true); // 计数+1
                        _resFacade.Destroy(_asset, false); //引用计数 -1
                        _asset = instantiatedAsset;
                        Profiler.EndSample();
                        Profiler.EndSample();
                    }

                    _status |= Status.AssetAccessed;
                }

                return _asset;
            }
        }

        public void OnAssetLoadComplete(UObject original)
        {
            _status |= Status.Done;
            _context = null;
            if (original != null)
            {
                //资源管理内部对资源引用计数 +1，不实例化
                _asset = _resFacade.Instantiate(original, _item, false);
            }
            else
            {
                _asset = null;
            }

            var c = callback;
            callback = null;
            if (c != null)
            {
                Profiler.BeginSample(c.Target != null ? c.Target.ToString() : "NoNameTarget");
                Profiler.BeginSample(c.Method.Name);
                // 延迟到回调当中，取AsyncResourceLoadRequest.asset时实例化。但这样没法把实例化和回调分帧
                c(this);
                Profiler.EndSample();
                Profiler.EndSample();
                
                // 加载回调完成后自动dispose，无回调不销毁，使用者不必每次手动调用
                Dispose();
            }
        }

        public void Dispose()
        {
            _status |= Status.Disposed;
            if (_context != null)
            {
                _context.Remove(this);
                _context = null;
            }

            callback = null;
            var tAsset = _asset;
            _asset = null;

            if ((_status & Status.AssetAccessed) == 0 && tAsset)
            {
                _resFacade.Destroy(tAsset, false);
            }
        }

#if UNITY_EDITOR
        ~AsyncResourceLoadRequest()
        {
            if ((_status & Status.Disposed) == 0)
            {
                UnityEngine.Debug.LogError(this._id + " has a leaked asyncResourceLoadRequest");
            }
        }
#endif
    }
}